import asyncio
from common.sys_settings import sysSetting
from settings import server
from common.data import net
import logging
import sys

fmt = '%(asctime)s - %(levelname)s: %(message)s'
logging.basicConfig(format=fmt, level=logging.INFO)
uid = [0, 0]


async def pipe(reader, writer):
    # 实现内容的转发
    try:
        while not reader.at_eof():
            data = await reader.read(sysSetting.bufsize)
            writer.write(data)
            await writer.drain()
    except Exception as e:
        logging.error(e)
    finally:
        logging.info('管道断开')
        writer.close()


async def start_pipe_request(data):
    """
    处理创建pipe请求
    """
    local_reader, local_writer = await asyncio.open_connection(
        server['local']['ip'],
        server['local']['port']
    )
    server_reader, server_writer = await asyncio.open_connection(
        server['remote']['ip'],
        server['remote']['port']
    )
    vid = data[0]
    peername = data[1]
    logging.info(f"{peername}({vid})加入连接")
    _message = net.encode((uid[0], vid), sysSetting.new_pipe, 'utf-8', sysSetting.separator)
    server_writer.write(_message)
    await server_writer.drain()

    task_list = [
        asyncio.create_task(pipe(local_reader, server_writer)),
        asyncio.create_task(pipe(server_reader, local_writer))
    ]
    await asyncio.wait(task_list)
    logging.info(f"{peername}({vid})断开连接")


async def parse_msg(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    """
    解析数据
    """
    _loop = 0
    try:
        while True:
            message = await reader.readuntil(sysSetting.separator)
            _type, _data = net.decode(message, 'utf-8', sysSetting.separator)
            if _type == sysSetting.pong:
                if _loop % sysSetting.when_log_ping == 0:
                    logging.info(f'受到代理服务器的响应({_loop})')
                    if _loop >= sysSetting.max_ping_loop:
                        _loop = 0
                _loop += 1
            elif _type == sysSetting.new_pipe:
                asyncio.create_task(start_pipe_request(_data))
            elif _type == sysSetting.get_host:
                uid[0] = _data[1]
                logging.info(f"服务器-{uid[0]}开放在{server['remote']['ip']}:{_data[0]}")
            elif _type == sysSetting.message:
                logging.info(_data)
            elif _type == sysSetting.error:
                logging.error(_data)
    except Exception as e:
        logging.error(e)
    finally:
        logging.info("关闭连接反向代理客户端")
        writer.close()


async def command():
    while True:
        _data = await asyncio.get_event_loop().run_in_executor(None, sys.stdin.readline)
        if _data.strip() == 'help':
            print("帮助:"
                  "\n\thelp -- 打开帮助"
                  "\n\tshow connector -- 显示当前访问内网服务器的用户"
                  "\n(按下ctrl+C结束服务器)")
        elif _data.strip() == 'show connector':
            reader, writer = await asyncio.open_connection(
                server['remote']['ip'],
                server['remote']['port']
            )
            _type = sysSetting.request_get_connector
            _message = net.encode(uid[0], _type, 'utf-8', sysSetting.separator)
            writer.write(_message)
            await writer.drain()
            _return = await reader.readuntil(sysSetting.separator)
            _, _data = net.decode(_return, 'utf-8', sysSetting.separator)
            logging.info(_data)
            writer.close()
        else:
            print("未知命令,如需要帮助请输入help")


async def create_tunnel(data):
    """
        构建隧道
        """
    try:
        reader, writer = await asyncio.open_connection(
            server['remote']['ip'],
            server['remote']['port']
        )
        logging.info(f"发送连接请求:{data}")
        writer.write(data)
        await writer.drain()
        logging.info(f'内网穿透服务器{writer.get_extra_info("peername")}连接成功')
        task_list = [
            asyncio.create_task(send_heart_beat(writer)),
            asyncio.create_task(parse_msg(reader, writer)),
            asyncio.create_task(command())
        ]
        await asyncio.wait(task_list)
    except Exception as e:
        logging.error(f"无法连接到内网穿透服务器,错误异常为:{e}")


async def send_heart_beat(writer: asyncio.StreamWriter):
    """
        发送心跳,保持连接
        """
    while True:
        _message = net.encode('', sysSetting.ping, 'utf-8', sysSetting.separator)
        writer.write(_message)
        await writer.drain()
        await asyncio.sleep(10)


async def run():
    _message = net.encode('', sysSetting.new_server, 'utf-8', sysSetting.separator)
    task_list = [
        asyncio.create_task(create_tunnel(_message), name='1')
    ]
    done, pending = await asyncio.wait(task_list, return_when=asyncio.FIRST_COMPLETED)  # 返回完成的任务和被挂起的任务(未成功完成的)
    while True:  # 可以实现多个服务器反向代理，但是现在只实现一个
        # 循环尝试,直到联通,如果异常断开再进行重连
        task_list = [task for task in pending]
        for task in done:
            task_id = int(task.get_name())
            logging.info(f"重发 {task_id}: {_message}")
            task_list.append(
                asyncio.create_task(create_tunnel(_message), name=str(task_id))
            )
        done, pending = await asyncio.wait(task_list, return_when=asyncio.FIRST_COMPLETED)


def main():
    asyncio.run(run())


if __name__ == '__main__':
    logging.info("开启客户端")
    main()
